import numpy
import sys,os,inspect
from PyQt4 import QtCore
from KTrade.KStockFeed import KStockFeed
from KModel.KSingleStock import KSingleStock
from KIndicators import KBaseIndicator
from KCharts.KGraphics.KGraphicsSceneSubViewModel import KGraphicsSceneSubViewModel
from KCharts.KGraphics.KBaseDisplayItem import KBaseDisplayItem
from KApp import KConstants as vm
from PyQt4.QtGui import QGraphicsObject,QPen,QBrush,QColor,QPolygonF
from PyQt4 import QtCore,QtGui,Qt
from PyQt4.QtCore import QPointF,QLineF
import numpy as np
from collections import deque
class KAtomStochOscillator(KBaseIndicator.KBaseIndicator):
	''' the atomic indicator to display stoch oscillator
	http://stockcharts.com/help/doku.php?id=chart_school:technical_indicators:stochastic_oscillato
	'''
	def __init__(self,parent=None):
		super(KAtomStochOscillator,self).__init__(parent)
		self.windowsize=14
		self.DWindow=3 # the window size of K to generate D signal
		

	def GetWindowSize(self):
		return self.windowsize
	def SetWindowSize(self,newsize):
		self.windowsize=newsize
	

	
	def GetKDSeries(self):
		return (self.Klines,self.Dlines)
	
	def GetLogicRange(self):
		# get the range of all data in this indicator

		if self.Klines:
			(xdatarange,temp)=self.m_stock.CurWindowRange('Price')			
			tempyseries=[ x for x in self.Klines+self.Dlines if x]
			ydatarange=[min(tempyseries), max(tempyseries)]
			return (xdatarange,ydatarange)
		
		return self.m_stock.CurWindowRange('Price')
	
	def calculate(self,**kargs):		
		


		self.OnStart()
		self.stockfeed=KStockFeed(self.m_stock)
		stockfeed=self.stockfeed
		while stockfeed.HasNextBar():
			stockfeed.MoveForward()
			self.OnNewBars(stockfeed.GetCurrentIndex())		
		self.OnFinish()
		
		



	
	## online functions
	def OnNewBars(self,curfeedindex):
		# used in online mode, will be called when a new bar is available
		
		# update buffer
		closep=self.stockfeed.GetBarByIndex(curfeedindex,'close')
		self.pricebuffer.append(closep)
		
		# compute osillator
		leftindex=curfeedindex-self.windowsize+1
		if leftindex<0:
			self.Klines.append(-1) # negative means nothing
			self.Dlines.append(-1) # negative means nothing
		else:
			# compute K
			curbuffer=self.pricebuffer[leftindex:]
			localmax=max(curbuffer)
			localmin=min(curbuffer)
			curK=(closep-localmin)/(localmax-localmin)*100
			assert(curK<=100 and curK>=0)
			self.Klines.append(curK)
			# compute D
			if len(self.Klines)<self.windowsize+self.DWindow:
				curD=-1 # negative means nothing
			else:
				curKBuffer=self.Klines[-self.DWindow:]
				curD=sum(curKBuffer)/self.DWindow
				assert(curD<=100 and curD>=0)
			self.Dlines.append(curD)
			
			
			
			


		

		
	def OnStart(self):
		# used in online mode, called before simulation started
		self.Klines=[]
		self.Dlines=[]
		self.pricebuffer=[]
		
	def OnFinish(self):
		# used in online mode, called after simulation exectued
		KBaseIndicator.KBaseIndicator.update(self)# update data after calcuation
		
		print(len(self.Dlines))
	
class KAtomStochOscillatorDisplayItem(KBaseIndicator.KBaseIndicatorDisplayItem):
	

	
	def PaintCurves(self,painter):
		# start painting		

		mpen=QPen()
		mpen.setStyle(QtCore.Qt.SolidLine)
		mpen.setWidthF(1.2)	


		
		klines,dlines=self.model.GetKDSeries()	

		XCordinates=self.m_stock.CurWindowData('Index')
		xcordrange=[min(XCordinates),max(XCordinates)]
		
		
		mpen.setColor(QColor(255,0,0))
		painter.setPen(mpen)
		painter.drawPath(self.GetCurvePath(xcordrange,klines))	# draw K line, the faster one
		mpen.setColor(QColor(0,255,0))
		painter.setPen(mpen)
		painter.drawPath(self.GetCurvePath(xcordrange,dlines))	# draw d line, the slower one,smoothed one
		
		mpen.setWidthF(0.8)	
		mpen.setStyle(QtCore.Qt.DashDotDotLine)
		mpen.setColor(QColor(0,255,255))
		painter.setPen(mpen)

		(xcordrange,ycords)=self.viewmodel.mapLogiceCordToScene(vm.RectType.ContentBound,xcordrange,[80,20])
		painter.drawLine(xcordrange[0],ycords[0],xcordrange[1],ycords[0]) # over bought line
		painter.drawLine(xcordrange[0],ycords[1],xcordrange[1],ycords[1]) # over sold line
			
	def GetCurvePath(self,xcordrange,klines):
		
		# only show curves in current window

		minx,maxx=xcordrange
		txcords=range(minx,maxx+1)
		tycords=klines[minx:maxx+1]
		xcords=[]
		ycords=[]
		for index in range(0, len(tycords)):
			if tycords[index]>0: # remove invalid points
				xcords.append(txcords[index])
				ycords.append(tycords[index])
		
		(xcords,ycords)=self.viewmodel.mapLogiceCordToScene(vm.RectType.ContentBound,xcords,ycords)
		
		mpath=QtGui.QPainterPath()
		mpath.moveTo(xcords[0],ycords[0])
		for index in range(1,len(ycords)):
			mpath.lineTo(xcords[index],ycords[index])	
		return mpath
	
			
	
		
	def PrepareRenderPicture(self):
		## pre-computing a QPicture object allows paint() to run much more quickly, 
		## rather than re-drawing the shapes every time.

		if not hasattr(self,'model') :
				return
		if not self.model.IsReady():
			return 		
		if self.model.IsResultExpired()==True:
			self.model.calculate()	
				
		self.picture = QtGui.QPicture()
		painter = QtGui.QPainter(self.picture)	



		self.PaintBorders(painter,vm.RectType.OuterBound)
		self.PaintBorders(painter,vm.RectType.InnerBound)
		self.PaintBorders(painter,vm.RectType.ContentBound)
		
		self.PaintXAxis(painter,vm.RectType.XAxisBottom,10)
		self.PaintYAxis(painter,vm.RectType.YAxisRight,2)

		self.PaintCurves(painter)
		
		painter.end()	
